<?php
JLoader::register("PCompanyModelThirdAccounts", OC::$SERVERROOT."/apps/company/lib/thirdaccounts.php");
JLoader::register("QueueHelper", JPATH_SITE.'/components/com_jobmail/helpers/queue.php');

JTable::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_puser/tables');
JTable::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_jobs/tables');

/**
 * Resumes controller
 */
class PResumesModelSearch extends PResumesModelResumes
{

   public function topSearch() {
      $timetop = JRequest::getString('timetop', null); 
      
      if($timetop) {
         return $this->topList($timetop, false);
      }

      return $this->getList();
   }

   public function topList($topnum=5, $setorderby=true) {
      JRequest::setVar('nopagination', 1);
      $app = PFactory::getApplication();
  
      $resumedir = $app->getUserState('resumedir', null);
      if(!$resumedir) {
          $resumedir = array();
      }

      // when setorderby is true or porderby is not set (from topsearch direct link click, we need to set orderby to matchresult  
      if($setorderby || !isset($resumedir['porderby'])) {
         $resumedir['orderby'] = "matchresult";
         $resumedir['orderdir'] = "DESC";
         $app->setUserState('resumedir', $resumedir);
      }
	
      JRequest::setVar('topnlist', 1); 
      return $this->getList(0, $topnum); 
   }

   public function setSearchTerms() {
      $app = PFactory::getApplication();

      $terms = JRequest::get("post");
 
      $source = empty($terms['source']) ? array() : $terms['source']; 

      $job_name = empty($terms['job_name']) ? '' : $terms['job_name'];
      $job_id = empty($terms['job_id']) ? '' : $terms['job_id'];
      $term_id = empty($terms['term_id']) ? 0 : $terms['term_id']; 

      // remove certain 
      unset($terms['source']);
      unset($terms['job_name']);
      unset($terms['job_id']);
      unset($terms['term_id']); 

      $terms = array_filter($terms);

      if (empty($terms)) {
         // If no terms, default search in last two month
         $terms['update_time'] = 8;
      }

      if (!empty($job_id)) {
         $terms['job_id'] = $job_id;
         $terms['job_name'] = $job_name;
      }


      // if no source is given, nothing needs to be done 
      if(empty($source)) {
         return false; 
      }

      $user = OC_USER::getUser();
      $master_user = PFactory::getMasterUser()->get('id');

      // nsource holds source status: whether a source needs to create delaytask or re-use existing one 
      $nsources = []; 

      // Insert terms into terms table
      $db = PFactory::getDbo();
      if(empty($term_id)) {
         // Put source back to terms
         $terms['source'] = $source;

         if (!empty($source)) {
            foreach ($source as $k => $v) {
               $terms[$v] = '1';
            }
         }

         // insert directly 
         $query = "INSERT INTO *PREFIX*hr_search_terms (`userid`, `owner`, `terms`, `createdate`) VALUES (".intval($master_user).", ".$db->quote($user).", ".$db->quote(json_encode($terms)).", UTC_TIMESTAMP)";
         $db->setQuery(PUtil::replaceOCPrefix($query));
         if (!$db->execute()) {
            return false;
         }
         $term_id = $db->insertid();

         // indicate whether the source needs to add delaytask. 1 is true to add; 0 is not use existing 
         foreach($source as $s) {
            $nsources[$s] = 1;
         }

      }
      else {
         // check if search term has been changed 
         $query = "SELECT terms, last_sync_result FROM *PREFIX*hr_search_terms WHERE id=" . $db->quote($term_id) . " AND owner = " . $db->quote($user);
         $db->setQuery(PUtil::replaceOCPrefix($query));
         $result = $db->loadObject(); 
         if(!$result)
            return false; 

         // need to find out if delaytask ids are still valid 
         $nodelaytask = false; 
         $last_sync_result = !empty($result->last_sync_result) ? json_decode($result->last_sync_result, true) : array();
         $delaytask_ids = []; 
         foreach($last_sync_result as $t=>$v) {
            if(!empty($v['delaytaskid'])) {
               $delaytask_ids[$t] = intval($v['delaytaskid']); 
            }
            else {
               $delaytask_ids[$t] = 0;
               $nodelaytask = true; 
            }   
         }

         if(count($delaytask_ids) > 0) {
            $query = "SELECT id FROM #__delaytasks WHERE id IN (" . implode(',', array_values($delaytask_ids)). ")"; 
            $db->setQuery(PUtil::replaceOCPrefix($query));
            $list = $db->loadAssocList(null, 'id');
            
            foreach($delaytask_ids as $t=>$v) {
               if(!in_array($v, $list)) {
                  $delaytask_ids[$t] = 0;
                  $nodelaytask = true; 
               }
            }
         }

         // check if term are still same or not 
         $prevterms = json_decode($result->terms, true);
         $prevsources = !empty($prevterms['source']) ? $prevterms['source'] : array();

         unset($prevterms['source']); 
         foreach ($prevsources as $k) {
            unset($prevterms[$k]); 
         }

         $nowterms = json_encode($terms); 

         // find out whether the source has been changed 
         $addsources =  array_diff($source, $prevsources);
         $removesources = array_diff($prevsources, $source); 


         // same search term or not 
         $same_term = json_encode($prevterms) == json_encode($terms); 

         // we compare the new terms with prev terms without source
         if(!$same_term || $nodelaytask || !empty($addsources) || !empty($removesources)) {
            // things are changed, reset content  

            // Put source back to terms
            $terms['source'] = $source;

            // add each type into term json 
            if (!empty($source)) {
               foreach ($source as $k => $v) {
                  $terms[$v] = '1'; 
               }
            }

            $query = "UPDATE *PREFIX*hr_search_terms SET createdate=UTC_TIMESTAMP, terms = " . $db->quote(json_encode($terms)) . 
                    " WHERE id=" . $db->quote($term_id) . " AND owner = " . $db->quote($user); 
         
            $db->setQuery(PUtil::replaceOCPrefix($query));
            if(!$db->execute()) {
               return false; 
            }

            // if there is no delaytask, we need to re-generate for each search type 
            if($nodelaytask) {
               foreach($source as $s) {
                  if(empty($delaytask_ids[$s])) {
                     $nsources[$s] = 1;
                     PResumesHelper::updateTermSyncStatus($term_id, $s);
                  }
                  else {
                     $nsources[$s] = 0;
                     PResumesHelper::updateTermSyncStatus($term_id, $s, 'restart', $same_term ? false : true);
                  }
               }
            }
            else {
               // might be same term or or new source added
               foreach($source as $s) {
                  if(in_array($s, $addsources)) {
                     // add a complete new source. all existing delaytask ids are good (nodelaytask is false)
                     $nsources[$s] = 1;
                     PResumesHelper::updateTermSyncStatus($term_id, $s);
                  }
                  else {
                     $nsources[$s] = 0; 
                     // re-start it with termchanged false/true message; 
                     PResumesHelper::updateTermSyncStatus($term_id, $s, 'restart', $same_term ? false : true);
                  }
               }
            }
            
            if(!empty($removesources)) {
              // change new added source task list 
               foreach($removesources as $s) {
                  PResumesHelper::updateTermSyncStatus($term_id, $s, 'remove');
               }
            }

         }
         else {
            // nothing was changed 
            foreach($source as $s) {
               $nsources[$s] = 0;
               // re-start it with termchanged false message 
               PResumesHelper::updateTermSyncStatus($term_id, $s, 'restart', false);
            }
         }
      }

      // Add action to session terms
      $app->setUserState('search_syncto', $nsources);
      return array('term_id'=>$term_id, 'job_id'=>$job_id, 'job_title'=>$job_name);
   }

   public function getSearchInfo($term_id) {
      $db = PFactory::getDbo();
      $user = OC_USER::getUser();

      if (!empty($term_id)) {
         $condition = " AND id = ".intval($term_id);
      } else {
         // let's see if this user is logged into some other places 
         $query = "SELECT COUNT(*) FROM #__session WHERE " . (PFactory::isMasterUser() ? "userid=" : "username=") . $db->quote($user);
         $db->setQuery($query);
         // if only one session, don't add checkout 
         if($db->loadResult() <= 1)
            $condition = " AND stopped = 0 ORDER BY createdate DESC LIMIT 1";
         else 
            $condition = " AND stopped = 0 AND checkout = 0 ORDER BY createdate DESC LIMIT 1";
      }

      $query = "SELECT * FROM *PREFIX*hr_search_terms WHERE owner = ".$db->quote($user).$condition;
      $db->setQuery(PUtil::replaceOCPrefix($query));
      $info = $db->loadAssoc();

      if (!empty($info)) {
         $info['terms'] = !empty($info['terms']) ? json_decode($info['terms'], true) : array();
         $info['last_sync_result'] = !empty($info['last_sync_result']) ? json_decode($info['last_sync_result'], true) : array();
         // Active this search term
         $query = "UPDATE *PREFIX*hr_search_terms SET stopped = 0, checkout=1 WHERE id = ".intval($info['id']). " AND owner = ".$db->quote($user);
         $db->setQuery(PUtil::replaceOCPrefix($query));
         $db->execute();
      }
      return $info;
   }

   public function getSearchResult($term_id, $last_sync_result) {
      if (empty($term_id) || empty($last_sync_result)) {
         return array();
      }

      $db = PFactory::getDbo();
      $master_user = PFactory::getMasterUser()->get('id');

      $conditions = array();
      foreach ($last_sync_result as $source => $v) {
         // we don't need to check $v['result'] here for below reasons: 
         // 1. if search restarted without term changed, the old result will be used if it's ready 
         // 2. if search restarted with term changed, result will be emptied out 
         // 3. if a pageturn is issued, if the result is there it will be used; otherwise it should wait for result 
         $conditions[] = " ( r.source = '".$source."' AND r.page_num = ".(!empty($v['nowpage']) ? $v['nowpage'] : 1).") ";
      }
      $conditions = implode(" OR ", $conditions);

      if (empty($conditions))
         return array();

      $query = "SELECT j.* FROM
         *PREFIX*hr_search_result r
         LEFT JOIN *PREFIX*hr_search_jobseeker j ON j.user_id = r.user_id AND j.source = r.source AND j.resume_code = r.resume_code
         WHERE r.term_id = ".intval($term_id)." AND r.user_id = ".$db->quote($master_user)." AND (".$conditions.") ORDER BY j.is_show DESC, j.update_time DESC";

      // although it's good idea to cache, whit fast changing search term and page, it won't add much benefit and only troubles for invalid data
      // disable it for now 
      return $this->_getSearchResult($query); 

      /*
      $cache = CacheHelper::getCache('app_resumes');
      $data = $cache->call(array($this, '_getSearchResult'), $query);
      CacheHelper::setTriggers($master_user, $cache, array('*PREFIX*hr_search_result', '*PREFIX*hr_search_jobseeker'));
      return $data;
      */
   }

   public function _getSearchResult($query) {
      $db = PFactory::getDbo();
      $db->setQuery(PUtil::replaceOCPrefix($query));
      $list = $db->loadAssocList();
      return $list ? $list : array();
   }

   public function checkSearchResult($term_id) {
      if (empty($term_id)) {
         return false;
      }

      $db = PFactory::getDbo();
      $master_user = PFactory::getMasterUser()->get('id');

      $query = "SELECT last_sync_result FROM *PREFIX*hr_search_terms WHERE id = ".intval($term_id)." AND userid = ".intval($master_user);
      $db->setQuery(PUtil::replaceOCPrefix($query));
      $last_sync_result = $db->loadResult();

      if (empty($last_sync_result)) {
         return false;
      }

      $last_sync_result = json_decode($last_sync_result, true);

      $delaytask_ids = [];
      $searchresult = []; 
      $nowpages = []; 
      foreach($last_sync_result as $t=>$v) {
         if(!empty($v['delaytaskid'])) {
            $delaytask_ids[$t] = intval($v['delaytaskid']); 
         }
         if (isset($v['result']) && $v['result'] == 'success') {
            $searchresult[$t] = '100'; 
         }     
         if (isset($v['nowpage'])) {
            $nowpages[$t] = intval($v['nowpage']);
         }
         else {
            $nowpages[$t] = 1; 
         }
      }

      if(empty($delaytask_ids)) {
         return false; 
      }

      // check nowpage result.
      $query = "SELECT r.source, COUNT(*) AS count FROM *PREFIX*hr_search_result AS r 
                LEFT JOIN *PREFIX*hr_search_jobseeker j ON j.user_id = r.user_id AND j.source = r.source AND j.resume_code = r.resume_code 
                WHERE r.term_id = ".intval($term_id); 
      $page_queries = array(); 
      foreach($nowpages as $t=>$v) {
         array_push($page_queries, "(r.source=" . $db->quote($t) . " AND r.page_num=" . $v . ')');
      }
      $query .=  " AND (" . implode(' OR ', $page_queries) . ") AND j.id IS NOT NULL GROUP BY r.source"; 

      $db->setQuery(PUtil::replaceOCPrefix($query));
      $nowpages_result = $db->loadAssocList('source', 'count');

      if(!empty($nowpages_result)) {
         foreach($nowpages_result as $t=>$v) {
            if($v > 0) 
               $searchresult[$t] = '100';
            elseif (isset($searchresult[$t])) 
               unset($searchresult[$t]);
         }
      }
      else {
         // nothing is ready 
         $searchresult = array();
      }

      if(count($delaytask_ids) == count($searchresult))
         return $searchresult; 

      // check individual tasks 
      $query = "SELECT id, status FROM #__delaytasks WHERE id IN (" . implode(',', array_values($delaytask_ids)). ")";
      $db->setQuery(PUtil::replaceOCPrefix($query));
      $list = $db->loadAssocList('id', 'status'); 

      if(empty($list)) {
         return false; 
      }

      // create status list with source=>status list 
      foreach($list as $id=>$status) {
         foreach($delaytask_ids as $s=>$v) {
            if($id == $v && !isset($searchresult[$s])) {
               // when we reach here, even if status is 100, result actually is not ready, but close to ready 
               $searchresult[$s] = $status >= 100 ? 99 : $status; 
               break; 
            }
         }
      }      

      return $searchresult; 
   }

   public function getTermsByJobid($jobid, $sources=array()) {
      if (empty($jobid)) {
         return false;
      }

      $match = array(
         'DEGREE'=>'degree_level',
         'WORK_EXP'=>'workexp',
         'SALARY'=>'expect_salary',
         'INDUSTRY'=>'industry',
         'SEX'=>'sex'
      );

      $thirdModel = new PCompanyModelThirdAccounts();
      $final_terms = array();
      $accounts = array();

      $sync_info = $thirdModel->getCompanyThirdToolsInfo();

      if (empty($sources)) {
         $account_info = $sync_info['login'] ? json_decode($sync_info['login'],true) : array();
         foreach ($account_info as $k => $v) {
            $accounts[$k] = 1;
         }
      }

      $app = PFactory::getApplication();
      $master_user = PFactory::getMasterUser()->get('id');
      $db = PFactory::getDbo();
      $query = "SELECT b.alias AS name, a.expected_answer AS value, j.job_title 
         FROM #__jobs_job AS j
         LEFT JOIN *PREFIX*hr_company_interview a ON a.job_id = j.id 
         LEFT JOIN *PREFIX*hr_interview_questions b ON b.id = a.interview_id
         WHERE j.id = ".intval($jobid);
      $db->setQuery(PUtil::replaceOCPrefix($query));
      $terms = $db->loadAssocList();

      if (!empty($terms)) {
         $skills = array();
         $jobname = false; 
         foreach ($terms as $term) {
            if(!$jobname) 
               $jobname = $term['job_title']; 

            if ($term['name'] == 'LOCATION') {
               $location = explode('-', $term['value']);
               $final_terms['state'] = $location[0];
               $final_terms['city'] = $location[1];
               $query = "SELECT provinceid, cityid FROM #__citylist WHERE city = ".$db->quote($final_terms['city']);
               $db->setQuery($query);
               $statecity = $db->loadAssoc();
               $final_terms['id_state'] = !empty($statecity['provinceid']) ? $statecity['provinceid'] : 0;
               $final_terms['id_city'] = !empty($statecity['cityid']) ? $statecity['cityid'] : 0;
            } elseif ($term['name'] == 'AGE') {
               $agerange = explode('-', $term['value']);
               $final_terms['agefrom'] = $agerange[0];
               $final_terms['ageto'] = $agerange[1];
            } elseif ($term['name'] == 'SKILL') {
               $skill = json_decode($term['value'], true);
               if (!empty($skill['skill'])) {
                  array_push($skills, $skill['skill']);
               }
            } else {
               if (array_key_exists($term['name'], $match)) {
                  $final_terms[$match[$term['name']]] = $term['value'];
               }
            }
         }

         if (!empty($skills)) {
            $final_terms['keyword'] = trim(implode(' ', $skills));
         }

         if (!empty($sources)) {
            foreach ($sources as $k => $v) {
               $final_terms[$v] = '1';
            }
         } else {
            $final_terms = array_merge($final_terms, $accounts);
         }

         $final_terms['job_id'] = intval($jobid);
         $final_terms['job_name'] = empty($jobname) ? '' : $jobname;
         return $final_terms;
      }

      // Can't find anything from oc_hr_company_interview, gather some infomation from jos_jobs_job
      $query = "SELECT a.id_degree_level AS degree_level, a.agefrom, a.ageto, a.salary AS expect_salary, a.id_state, a.id_city, a.state, a.city, b.min_years AS workexp, c.min_salary AS expect_salary
         FROM #__jobs_job a
         LEFT JOIN #__jobs_job_exp b ON b.id = a.id_job_exp
         LEFT JOIN #__jobs_salary_type c ON c.id = a.id_salary_type
         WHERE a.id = ".intval($jobid)." AND employer_id = ".$db->quote($master_user);
      $db->setQuery($query);
      $terms = $db->loadAssoc();

      if (!empty($terms)) {
         foreach ($terms as $k => $v) {
            if (empty($v)) {
               unset($terms[$k]);
            }
         }

         if (!empty($sources)) {
            foreach ($sources as $k => $v) {
               $terms[$v] = '1';
            }
         } else {
            $terms = array_merge($terms, $accounts);
         }

         $terms['job_id'] = intval($jobid);
         $terms['job_name'] = empty($jobname) ? '' : $jobname;
         return $terms;
      }

      if (!empty($sources)) {
         foreach ($sources as $k => $v) {
            $terms[$v] = '1';
         }
      } else {
         $terms = array_merge($terms, $accounts);
      }

      $terms['job_id'] = intval($jobid);
      $terms['job_name'] = empty($jobname) ? '' : $jobname;
      return $terms; 
   }

   public function killProcessByTermid($term_id) {
      if (empty($term_id))
         return false;

      $db = PFactory::getDbo();
      $user = OC_USER::getUser();

      $query = "SELECT count(*) FROM *PREFIX*hr_search_terms WHERE stopped = 0 AND owner = ".$db->quote($user)." AND id <> ".intval($term_id);
      $db->setQuery(PUtil::replaceOCPrefix($query));
      $count = $db->loadResult();

      if ( $count > 0 ) {
         // Set this term_id to stopped
         $query = "UPDATE *PREFIX*hr_search_terms SET stopped = 1 WHERE owner = ".$db->quote($user)." AND id = ".intval($term_id);
         $db->setQuery(PUtil::replaceOCPrefix($query));
         if (!$db->execute()) {
            return false;
         }

         $query = "SELECT last_sync_result FROM *PREFIX*hr_search_terms WHERE id = ".intval($term_id)." AND owner = ".$db->quote($user);
         $db->setQuery(PUtil::replaceOCPrefix($query)); 
         $db->execute(); 
         $last_sync_result = $db->loadResult(); 

         if($last_sync_result) {
            $delaytask_ids = []; 
            $last_sync_result = json_decode($last_sync_result, true); 
            foreach($last_sync_result as $t=>$v) {
               if(!empty($v['delaytaskid'])) { 
                  array_push($delaytask_ids, $v['delaytaskid']); 
               }
            }
            // kill job is delayed job 
            PResumesHelper::updateSearchTask('kill', $term_id, null, $delaytask_ids, false, true);
         }
      }

      return true; 
   }

   public function turnPage($term_id, $source, $page_num, $delaytask_id) {
      if (empty($source) || empty($page_num) || empty($term_id))
         return false;

      $db = PFactory::getDbo();
      $user = OC_USER::getUser();
      $master_user = PFactory::getMasterUser()->get('id');

      // Check if background search is ready?
      $query = "SELECT count(*) FROM *PREFIX*hr_search_result WHERE source = ".$db->quote($source)." AND page_num = ".intval($page_num)." AND user_id = ".intval($master_user)." AND term_id = ".intval($term_id);
      $db->setQuery(PUtil::replaceOCPrefix($query));
      $count = $db->loadResult();

      // Get this term's last_sync_result
      $query = "SELECT last_sync_result FROM *PREFIX*hr_search_terms WHERE id = ".intval($term_id)." AND owner = ".$db->quote($user);
      $db->setQuery(PUtil::replaceOCPrefix($query));
      $last_sync_result = $db->loadResult();

      $last_sync_result = json_decode($last_sync_result, true);

      // Update last_sync_result (Only update result to 'syncing' when data is not ready). page_num should be smaller than totalpages 
      $totalpages =  $last_sync_result[$source]['totalpages'] ? $last_sync_result[$source]['totalpages'] : 1; 
      $last_sync_result[$source]['nowpage'] = $page_num > $totalpages ? $totalpages : $page_num;
      if ( $count > 0 ) {
         $last_sync_result[$source]['result'] = 'success';
      }
      else {
         // FIXME: this might set race condition when synctool might update it at same time 
         // the turnpage beanstalkd job will handle this result change 
         $last_sync_result[$source]['result'] = 'syncing';

         // we only reset status here. All other parameter will be handled by syncmonitor
         // set status to not 0 to avoid killed by delaytaskworker monitoring 
         $query = "UPDATE #__delaytasks SET status=1 WHERE id = " . intval($last_sync_result[$source]['delaytaskid']);
         $db->setQuery(PUtil::replaceOCPrefix($query));
         $db->execute();
      }

      // Update last_sync_result
      $query = "UPDATE *PREFIX*hr_search_terms SET last_sync_result = ".$db->quote(json_encode($last_sync_result))." WHERE id = ".intval($term_id)." AND owner = ".$db->quote($user);
      $db->setQuery(PUtil::replaceOCPrefix($query));
      if (!$db->execute()) {
         return false;
      }

      // If there is some data in our database, just show
      if ( $count > 0 ) {
         return true;
      } else {
         if (empty($delaytask_id)) {
            return false;
         }
         // Add task to beanstalkd tube
         try {
            $queue = new QueueHelper();
            $data = array(); 
            $data['action'] = 'turnpage';
            $data['page_num'] = $page_num;
            # for consistency, delaytask_ids is an array 
            $data['delaytask_ids'] = array($delaytask_id);

            $tubename = 'localsearchmonitorTube';

            $config = PFactory::getConfig();
            // need to check if global queue enabled. If not, everything should be in localhost beanstalkd 
            if($config->get('globalqueue')) {
               $task = array();
               $task['tube'] = $tubename;
               $task['data'] = $data;
               $task['companyid'] = PFactory::getMasterUser()->id;
               $data = $task;
               $tubename = 'globaltaskTube';
            }

            $queue->putTask($data, $tubename, 0);
         } catch(Exception $e) {}
         
         return true;
      }

      return false;
   }

}
